/******************************************/
/*
playsaw.cpp
by Gary P. Scavone, 2006

This program will output sawtooth waveforms
of different frequencies on each channel.
*/
/******************************************/

#include "RtAudio.h"
#include <iostream>
#include <cstdlib>

#define FORMAT RTAUDIO_SINT16
#define SCALE  32767.0

// Platform-dependent sleep routines.
#if defined( __WINDOWS_ASIO__ ) || defined( __WINDOWS_DS__ )
#include <windows.h>
#define SLEEP( milliseconds ) Sleep( (DWORD) milliseconds ) 
#else // Unix variants
#include <unistd.h>
#define SLEEP( milliseconds ) usleep( (unsigned long) (milliseconds * 1000.0) )
#endif

#define BASE_RATE 0.005
#define TIME   1.0

unsigned int channels;
RtAudio::StreamOptions options;
unsigned int frameCounter = 0;
bool checkCount = false;
unsigned int nFrames = 0;
const unsigned int callbackReturnValue = 1;

int saw( void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames,
	double streamTime, RtAudioStreamStatus status, void *data )
{
	unsigned int i, j;
	extern unsigned int channels;
	short *buffer = (short *) outputBuffer;
	double *lastValues = (double *) data;

	if ( status )
		std::cout << "Stream underflow detected!" << std::endl;

	double increment;
	for ( j=0; j<channels; j++ ) {
		increment = BASE_RATE * (j+1+(j*0.1));
		for ( i=0; i<nBufferFrames; i++ ) {
			*buffer++ = (short) (lastValues[j] * SCALE);
			lastValues[j] += increment;
			if ( lastValues[j] >= 1.0 ) lastValues[j] -= 2.0;
		}
	}

	frameCounter += nBufferFrames;
	if ( checkCount && ( frameCounter >= nFrames ) ) return callbackReturnValue;
	return 0;
}

int main( int argc, char *argv[] )
{
	unsigned int bufferFrames, fs, device = 0, offset = 0;

	RtAudio dac;
	if ( dac.getDeviceCount() < 1 ) {
		std::cout << "\nNo audio devices found!\n";
		exit( 1 );
	}

	channels = (unsigned int) atoi( argv[1] );
	fs = (unsigned int) atoi( argv[2] );
	if ( argc > 3 )
		device = (unsigned int) atoi( argv[3] );
	if ( argc > 4 )
		offset = (unsigned int) atoi( argv[4] );
	if ( argc > 5 )
		nFrames = (unsigned int) (fs * atof( argv[5] ));
	if ( nFrames > 0 ) checkCount = true;

	double *data = (double *) calloc( channels, sizeof( double ) );

	// Let RtAudio print messages to stderr.
	dac.showWarnings( true );

	// Set our stream parameters for output only.
	bufferFrames = 256;
	RtAudio::StreamParameters oParams;
	oParams.deviceId = device;
	oParams.nChannels = channels;
	oParams.firstChannel = offset;

	options.flags |= RTAUDIO_HOG_DEVICE;
	options.flags |= RTAUDIO_SCHEDULE_REALTIME;
	options.flags |= RTAUDIO_NONINTERLEAVED;

	try {
		dac.openStream( &oParams, NULL, FORMAT, fs, &bufferFrames, &saw, (void *)data, &options );
		dac.startStream();
	}
	catch ( RtError& e ) {
		e.printMessage();
		goto cleanup;
	}

	if ( checkCount ) {
		while ( dac.isStreamRunning() == true ) SLEEP( 100 );
	}
	else {
		char input;
		std::cout << "Stream latency = " << dac.getStreamLatency() << "\n" << std::endl;
		std::cout << "\nPlaying ... press <enter> to quit (buffer size = " << bufferFrames << ").\n";
		std::cin.get( input );

		try {
			// Stop the stream
			dac.stopStream();
		}
		catch ( RtError& e ) {
			e.printMessage();
		}
	}

cleanup:
	if ( dac.isStreamOpen() ) dac.closeStream();
	free( data );

	return 0;
}
